package com.itheima.filter;

import com.itheima.balance.GrayLoadBalancer;
import com.itheima.ip.ClientIp;
import com.itheima.ip.IpInfo;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerUriTools;
import org.springframework.cloud.client.loadbalancer.reactive.DefaultRequest;
import org.springframework.cloud.client.loadbalancer.reactive.Response;
import org.springframework.cloud.gateway.config.LoadBalancerProperties;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.support.DelegatingServiceInstance;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.http.HttpHeaders;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.net.URI;
import java.util.function.Consumer;

/*****
 * @Author:
 * @Description:全局过滤器，拦截所有用户请求，按需进行路由
 ****/
@Configuration
public class GrayFilter implements GlobalFilter, Ordered {
    
    //负载均衡器工厂对象，可以加载指定服务列表
    private final LoadBalancerClientFactory clientFactory;
    //负载均衡属性对象
    private LoadBalancerProperties properties;
    
    /***
     * 创建负载均衡器工厂对象
     * @param clientFactory
     * @param properties
     */
    public GrayFilter(LoadBalancerClientFactory clientFactory, LoadBalancerProperties properties) {
        this.clientFactory = clientFactory;
        this.properties = properties;
    }
    
    /*****
     * 拦截所有请求
     * @param exchange：用户请求响应的所有对象
     * @param chain：链路调用对象
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //获取scheme，bootstrap.yml中 uri: scheme://xxx
        URI uri = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR);
        String schemePrefix = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_SCHEME_PREFIX_ATTR);
        
        //判断当前scheme是否为灰度服务，我们定义灰度服务的scheme为grayLb
        if (uri != null && ("grayLb".equals(uri.getScheme()) || "grayLb".equals(schemePrefix))) {
            //根据不同策略获取灰度服务实例
            Mono<Response<ServiceInstance>> responseInstance = this.choose(exchange);
            
            //封装灰度实例地址信息（将 http://car-version1/car 换成 http://192.168.23.1:18082/car）
            responseInstance = setInstanceInfo(responseInstance, exchange, schemePrefix);
            //灰度服务链路调用
            return responseInstance.then(chain.filter(exchange));
        }
        return chain.filter(exchange);
    }
    
    /****
     * 封装灰度实例地址信息（将 http://car-version1/car 换成 http://192.168.23.1:18082/car）
     */
    public Mono<Response<ServiceInstance>> setInstanceInfo(
            Mono<Response<ServiceInstance>> responseInstance,
            ServerWebExchange exchange,
            String schemePrefix) {
        return responseInstance.doOnNext(new Consumer<Response<ServiceInstance>>() {
            @Override
            public void accept(Response<ServiceInstance> instance) {
                //实例存在
                if (instance.hasServer()) {
                    //获取URI
                    URI uri = exchange.getRequest().getURI();
                    String overrideScheme = null;
                    if (schemePrefix != null) {
                        overrideScheme = uri.getScheme();
                    }
                    //创建链路调用委托对象
                    DelegatingServiceInstance serviceInstance = new DelegatingServiceInstance(instance.getServer(), overrideScheme);
                    //将实例路径换成实例IP真实地址（grayLb://car-version/car-->http://192.168.23.1:18081/car）
                    URI requestURI = LoadBalancerUriTools.reconstructURI(serviceInstance, uri);
                    exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR, requestURI);
                }
            }
        });
    }
    
    /***
     * 调用GrayLoadBalancer的choose()方法，获取不同服务实例
     * @return
     */
    public Mono<Response<ServiceInstance>> choose(ServerWebExchange exchange) {
        //获取URI，URI中包含了要调用的服务集群集合信息，我们需要把它传到GrayLoadBalancer中
        //在GrayLoadBalancer中可以根据集合进行筛选
        URI uri = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR);
        GrayLoadBalancer grayLoadBalancer =
                new GrayLoadBalancer(
                        //用工厂对象加载可用的服务列表
                        clientFactory.getLazyProvider(uri.getHost(), ServiceInstanceListSupplier.class),
                        uri.getHost());
        if (grayLoadBalancer == null) {
            throw new RuntimeException("没有找到可用的实例对象");
        }
        //获得城市编码
        cityCode(exchange);
        //因为会有根据版本信息或者用户身份信息或者IP实现,我们可以把请求头信息封装到Request中
        HttpHeaders headers = exchange.getRequest().getHeaders();
        //调用GrayLoadBalancer的choose()获取实例
        return grayLoadBalancer.choose(new DefaultRequest<HttpHeaders>(headers));
    }
    
    /****
     * 获得城市编码
     */
    public void cityCode(ServerWebExchange exchange) {
        //获取请求头中的城市编码
        String city = exchange.getRequest().getHeaders().getFirst("city");
        if (StringUtils.isEmpty(city)) {
            //获取用户IP
            String ip = ClientIp.getIP(exchange.getRequest());
            
            //先查询本地，本地没有再查询百度API并将没有的IP添加到数据库中
            //本地数据库操作这里就不做演示了
            city = IpInfo.getIpAddresses(ip);
            if (!StringUtils.isEmpty(city)) {
                //添加到头中，后面根据头中city进行服务筛选
                exchange.getRequest().mutate().header("city", city);
                //第1次访问，获取IP，以后每次访问携带城市编号进来
                exchange.getResponse().getHeaders().add("city", city);
            }
        }
    }
    
    //最后执行
    @Override
    public int getOrder() {
        return Ordered.LOWEST_PRECEDENCE;
    }
}
